using System;
using System.Collections.Generic;
using System.Text;

namespace DataPaper
{

    class SmallError
    {
        public readonly Point p;
        public readonly bool correctedToB;
        public readonly double u;
        public SmallError(Point p, bool correctedToB, double u)
        {
            this.p = p;
            this.correctedToB = correctedToB;
            this.u = u;
        }
    }
    class LargeError
    {
        public readonly Point p;
        public readonly double u_r;
        public readonly double u_c;
        public readonly bool[,] block;
        public LargeError(Point p, double u_r, double u_c, bool[,] block)
        {
            this.p = p;
            this.u_r = u_r;
            this.u_c = u_c;
            this.block = block;
        }
    }

    static class Block
    {
        public static int
            Extract(Border2 bd, out List<byte> lst,
            out List<SmallError> smallError,
            out List<LargeError> largeErrors)
        {
            int error = 0;
            lst = new List<byte>();
            smallError = new List<SmallError>();
            largeErrors = new List<LargeError>();
            for (int b_r = 0; b_r < bd.nbBlock_r; ++b_r)
                for (int b_c = 0; b_c < bd.nbBlock_c; ++b_c)
                {
                    Point p;
                    double u_r; double u_c;
                    double b; double w;
                    double bl;
                    bd.Info(b_r, b_c,
                        out p, out u_r, out u_c,
                        out b, out w,
                        out bl);
                    bool[,] block = ReadBlock(p,
                            u_r, u_c, w, b, bl, bd.bw);
                    int error_r, error_c;
                    int e = CorrectBlock(block,
                        out error_r, out error_c);
                    
                    error += e;
                    if (e == 1)
                        smallError.Add(new SmallError(new Point(p.r + u_r * error_r,
                            p.c + u_c * error_c), block[error_r, error_c], 0.5*(u_r+u_c)));
                    else if (e > 1)
                        largeErrors.Add(new LargeError(p, u_r, u_c, block));
                    lst.AddRange(BoolToBytes(block));
                }
            return error;
        }


        static bool[,] ReadBlock(Point topLeftB,
            double u_r, double u_c, double w, double b, double bl,
            BW bw
            )
        {
            bool[,] res = new bool[17, 17];
            double u = (u_r + u_c) / 2;
            for (int r = 0; r < 17; ++r)
                for (int c = 0; c < 17; ++c)
                {
                    res[r, c] = bw.IsBlack(
                        (int)Math.Round(topLeftB.r + u_r * r),
                        (int)Math.Round(topLeftB.c + u_c * c),
                        w, b, (int)bl, u);
                }
            res[17 / 2, 17 / 2] = res[17 - 1, 17 - 1];
            return res;
        }
        public static int CorrectBlock(bool[,] block,
            out int rSmallError,
            out int cSmallError)
        {
            bool[,] res = new bool[17, 17];
            List<int> errors_r = new List<int>();
            for (int r = 0; r < 16; ++r)
            {
                bool p = true;
                for (int c = 0; c < 16; ++c)
                    p = p ^ block[r, c];
                if (p != block[r, 16])
                    errors_r.Add(r);
            }
            List<int> errors_c = new List<int>();
            for (int c = 0; c < 16; ++c)
            {
                bool p = true;
                for (int r = 0; r < 16; ++r)
                    p = p ^ block[r, c];
                if (p != block[16, c])
                    errors_c.Add(c);
            }
            if (errors_c.Count == 1 && errors_r.Count == 1)
            {
                rSmallError = errors_r[0];
                cSmallError = errors_c[0];
                block[rSmallError, cSmallError] = !block[rSmallError, cSmallError];
                return 1;
            }
            rSmallError = -1;
            cSmallError = -1;
            if (errors_c.Count == 0 && errors_r.Count == 0)
                return 0;
            else
             return 1000;
        }

        static List<byte> BoolToBytes(bool[,] block)
        {
            List<byte> l = new List<byte>(8);
            for (int r = 0; r < 16; ++r)
            {
                byte b = 0;
                for (int c = 7; c >= 0; --c)
                    b = (byte)((b << 1) + (block[r, c] ? 1 : 0));
                l.Add(b);
                b = 0;
                for (int c = 15; c >= 8; --c)
                    b = (byte)((b << 1) + (block[r, c] ? 1 : 0));
                l.Add(b);
            }
            return l;
        }
    }
}
